deferred class CD_CLIENT_IMAGES
-- There are 2 kinds of client images: RGB and Indexed RGB (or MAP). The RGB 
-- image is composed by 3 buffers: red, green and blue (more colors, more 
-- memory). The MAP image is composed by 1 buffer of indices for a table and 
-- one table of encoded RGB values (less colors, less memory).
--
-- The image buffer is described by its width and height in pixels. The 
-- starting point of the buffer is the origin of the image, which is located at 
-- its bottom left corner. To retrieve a pixel in the image, use the formula 
-- pixel(x,y)=buffer[y*width + x].
--
-- The Put functions may do zoom in or out; zero order interpolation is used to 
-- scale the image. It is not possible to specify a part of the image to be 
-- drawn.	

inherit
	CANVAS_DRAW

feature {ANY}

	get_image_rgb (x, y, width, height: INTEGER): TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]
		-- Returns the red, green and blue components of each pixel in a server 
		-- image. The RGB components are provided in three arrays. The (i,j) 
		-- component of these matrices is at the address (j*w+i). As occurs with 
		-- all primitives from the Canvas Draw library, the pixel (0,0) is at the 
		-- bottom left corner, and the pixel (w-1,h-1) is that the upper right 
		-- corner of the image rectangle.
		local
			p1, p2, p3: POINTER
		do
			int_canvas_get_image_rgb(cnvs, p1, p2, p3, x, y, width, height)

			Result := [convert_to_array(p1, width, height),
			           convert_to_array(p2, width, height),
			           convert_to_array(p3, width, height)]
		end

	get_wd_image_rgb (x, y: REAL_64; width, height: INTEGER): TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]
		-- Like "get_image_rgb" but using world coordinates (REAL_64 values).
		local
			p1, p2, p3: POINTER
		do
			int_wd_canvas_get_image_rgb(cnvs, p1, p2, p3, x, y, width, height)

			Result := [convert_to_array(p1, width, height),
						  convert_to_array(p2, width, height),
						  convert_to_array(p3, width, height)]
		end

	put_image_rect_rgb (image_width, image_height: INTEGER; colors: TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]; x, y, w, h, xmin, xmax, ymin, ymax: INTEGER)
		-- Puts, in a specified area of the canvas, an image with its red, green 
		-- and blue components defined in the three matrices stored in byte 
		-- arrays. The (i,j) component of these matrices is at the address 
		-- (j*iw+i). The pixel (0,0) is at the bottom left corner, and the pixel 
		-- (iw-1,ih-1) is that the upper right corner of the image rectangle.
		--
		-- Parameters w and h refer to the target rectangle of the canvas, so 
		-- that it is possible to reduce or expand the image drawn. If w and h 
		-- are 0, the size of the image is assumed (iw and ih).
		--
		-- It also allows specifying a rectangle inside the image to be drawn, if 
		-- xmin, xmax, ymin and ymax are 0 then the whole image is assumed.
		--
		-- If the driver has bpp <=8 or only 256 colors or less, then the image 
		-- is converted to 256 optimal colors using the function "rgb_to_map" and 
		-- is drawn using "put_image_rect_map".
		do
			int_canvas_put_image_rgb(cnvs, image_width, image_height,
			convert_to_character_array(colors.item_1, image_width, image_height),
			convert_to_character_array(colors.item_2, image_width, image_height),
			convert_to_character_array(colors.item_3, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_image_rect_rgb_real (image_width, image_height: INTEGER; colors: TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		-- Like "put_image_rect_rgb" but using REAL_64 values.
		do
			int_canvas_c_double_put_image_rgb(cnvs, image_width, image_height,
			convert_to_character_array(colors.item_1, image_width, image_height),
			convert_to_character_array(colors.item_2, image_width, image_height),
			convert_to_character_array(colors.item_3, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_wd_image_rect_rgb (image_width, image_height: INTEGER; colors: TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		-- Like "put_image_rect_rgb" but using world coordinates (REAL_64 values).
		do
			int_wd_canvas_put_image_rgb(cnvs, image_width, image_height,
			convert_to_character_array(colors.item_1, image_width, image_height),
			convert_to_character_array(colors.item_2, image_width, image_height),
			convert_to_character_array(colors.item_3, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_image_rect_rgba (image_width, image_height: INTEGER; colors: TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]; x, y, w, h, xmin, xmax, ymin, ymax: INTEGER)
		-- The same as function "put_image_rect_rgb", except for the fact that it 
		-- is	possible to specify an alpha channel. The resulting color is the 
		-- image color weighted by the alpha value, using the formula 
		-- result=(source * alpha + destiny * (255 - alpha))/255. This means 
		-- that, if alpha is 0, the resulting color is the target color 
		-- (completely transparent), and, if alpha is 255, the resulting color is 
		-- the original image color (completely opaque).
		--
		-- If this function is not defined for a given driver or if alpha is 
		-- Void, then the function "put_image_rect_rgb" is used, as long as it is 
		-- defined.
		do
			int_canvas_put_image_rgba(cnvs, image_width, image_height,
			convert_to_character_array(colors.item_1, image_width, image_height),
			convert_to_character_array(colors.item_2, image_width, image_height),
			convert_to_character_array(colors.item_3, image_width, image_height),
			convert_to_character_array(colors.item_4, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_image_rect_rgba_real (image_width, image_height: INTEGER; colors: TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		-- Like "put_image_rect_rgb" but using REAL_64 values.
		do
			int_canvas_c_double_put_image_rgba(cnvs, image_width, image_height,
			convert_to_character_array(colors.item_1, image_width, image_height),
			convert_to_character_array(colors.item_2, image_width, image_height),
			convert_to_character_array(colors.item_3, image_width, image_height),
			convert_to_character_array(colors.item_4, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_wd_image_rect_rgba (image_width, image_height: INTEGER; colors: TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		-- Like "put_image_rect_rgb" but using world coordinates (REAL_64 values).
		do
			int_wd_canvas_put_image_rgba(cnvs, image_width, image_height,
			convert_to_character_array(colors.item_1, image_width, image_height),
			convert_to_character_array(colors.item_2, image_width, image_height),
			convert_to_character_array(colors.item_3, image_width, image_height),
			convert_to_character_array(colors.item_4, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_image_rect_map (image_width, image_height: INTEGER; index: ARRAY[INTEGER_8]; colors: ARRAY[INTEGER]; x, y, w, h, xmin, xmax, ymin, ymax: INTEGER)
		-- The same as function "put_image_rect_rgb", except for the fact that 
		-- the colors are provided by means of an index matrix (map). The color 
		-- corresponding to a given index is given in colors[index]. The map is 
		-- also a matrix stored as a byte vector. If the color vector is Void, 
		-- then a vector with 256 gray tones is assumed.
		do
			int_canvas_put_image_map(cnvs, image_width, image_height,
			convert_to_character_array(index, image_width, image_height),
			convert_to_integer_array(colors, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_image_rect_map_real (image_width, image_height: INTEGER; index: ARRAY[INTEGER_8]; colors: ARRAY[INTEGER]; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		-- Like "put_image_rect_map" but using REAL_64 values.
		do
			int_canvas_c_double_put_image_map(cnvs, image_width, image_height,
			convert_to_character_array(index, image_width, image_height),
			convert_to_integer_array(colors, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	put_wd_image_rect_map (image_width, image_height: INTEGER; index: ARRAY[INTEGER_8]; colors: ARRAY[INTEGER]; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		-- Like "put_image_rect_map" but using world coordinates (REAL_64 values).
		do
			int_wd_canvas_put_image_map(cnvs, image_width, image_height,
			convert_to_character_array(index, image_width, image_height),
			convert_to_integer_array(colors, image_width, image_height),
			x, y, w, h, xmin, xmax, ymin, ymax)
		end

	-- Skeep for the moment the imimage features. Instead add 
	-- IUP_IMAGE features.

	get_iup_image (x, y, width, height: INTEGER): IUP_IMAGE
		-- The same as the "get_image_rgb" functions except for the fact that use 
		-- an IUP_IMAGE object.
		local
			imimg, iupimg: POINTER
			image: IUP_IMAGE
		do
			imimg := int_im_image_create (width, height, 0, 0)
			int_canvas_get_im_image(cnvs, imimg, x, y)
			iupimg := int_iup_image_from_im_image(imimg)
			create image.with_internal(iupimg)
			int_im_image_destroy(iupimg)
			
			Result := image
		end

	get_wd_iup_image (x, y: REAL_64; width, height: INTEGER): IUP_IMAGE
		-- The same as "get_iup_image" but with World Coordinates.
		local
			imimg, iupimg: POINTER
			image: IUP_IMAGE
		do
			imimg := int_im_image_create (width, height, 0, 0)
			int_wd_canvas_get_im_image(cnvs, imimg, x, y)
			iupimg := int_iup_image_from_im_image(imimg)
			create image.with_internal(iupimg)
			int_im_image_destroy(iupimg)
			
			Result := image
		end

	put_iup_image (image: IUP_IMAGE; x, y: INTEGER)
		-- The same as the above functions except for the fact that use an 
		-- IUP_IMAGE object. Image must be a displayable image and it can has an 
		-- alpha channel.
		local
			p, img: POINTER
		do
			p := image.get_wid.deep_twin
			img := int_get_native_handle_image(p)
			int_canvas_put_im_image(cnvs, img, x, y,
			                        image.get_width,
			                        image.get_height)
		end

	put_iup_image_real (image: IUP_IMAGE; x, y: REAL_64)
		-- Like "put_iup_image" but using REAL_64 values.
		local
			p, img: POINTER
		do
			p := image.get_wid.deep_twin
			img := int_get_native_handle_image(p)
			int_canvas_c_double_put_im_image(cnvs, img, x, y,
			                                 image.get_width.to_real_64,
			                                 image.get_height.to_real_64)
		end

	put_wd_iup_image (image: IUP_IMAGE; x, y: REAL_64)
		-- Like "put_iup_image" but using World Coordinates.
		local
			p, img: POINTER
		do
			p := image.get_wid.deep_twin
			img := int_get_native_handle_image(p)
			int_wd_canvas_put_im_image(cnvs, img, x, y,
			                           image.get_width.to_real_64,
			                           image.get_height.to_real_64)
		end

	rgb_to_map (image_width, image_height: INTEGER; rgb_arrays: TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8], ARRAY[INTEGER_8]]; palette_size: INTEGER): TUPLE[ARRAY[INTEGER_8], ARRAY[INTEGER_8]]
		-- Converts an RGB image into an image with 256 indexed colors. The 
		-- resulting image must have the same size (width x height) as the RGB 
		-- image.
		local
			index, colors: POINTER
		do
			int_rgb_to_map(image_width, image_height, rgb_arrays.item_1.to_external, rgb_arrays.item_2.to_external, rgb_arrays.item_3.to_external, index, palette_size, colors)

			Result := [convert_to_array(index, image_width, image_height),
			           convert_to_array(colors, 1.to_integer_32, palette_size)]
		end

feature {}

	-- Converts

	convert_to_array (p: POINTER; w, h: INTEGER): ARRAY[INTEGER_8]
		local
			i, size: INTEGER
			m: NATIVE_ARRAY[CHARACTER]
			a: ARRAY[INTEGER_8]
		do
			if p.is_not_null then
				size := w*h - 1
				m := m.calloc(size)
				m := m.from_pointer(p)

				from
					i := 0
				until
					i = size
				loop
					a.put(m.item(i).to_integer_8, i)
					i := i + 1
				end

				Result := a
			end
		end

	convert_to_character_array (p: ARRAY[INTEGER_8]; w, h: INTEGER): POINTER
		local
			i, size: INTEGER
			m: NATIVE_ARRAY[CHARACTER]
		do
			if p /= Void then
				size := w*h - 1
				m := m.calloc(size)
			
				from
					i := 0
				until
					i = size
				loop
					m.put(p.item(i).to_character, i)
					i := i + 1
				end

				Result := m.to_external
			end
		end

	convert_to_integer_array (p: ARRAY[INTEGER]; w, h: INTEGER): POINTER
		local
			i, size: INTEGER
			m: NATIVE_ARRAY[INTEGER]
		do
			if p /= Void then
				size := w*h - 1
				m := m.calloc(size)
			
				from
					i := 0
				until
					i = size
				loop
					m.put(p.item(i), i)
					i := i + 1
				end

				Result := m.to_external
			end
		end

	-- Internals

	int_canvas_get_image_rgb(wgt, r, g, b: POINTER; x, y, iw, ih: INTEGER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "cdCanvasGetImageRGB"
         }"
      end

	int_wd_canvas_get_image_rgb(wgt, r, g, b: POINTER; x, y: REAL_64; iw, ih: INTEGER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "wdCanvasGetImageRGB"
         }"
      end

	int_canvas_put_image_rgb(wgt: POINTER; iw, ih: INTEGER; r, g, b: POINTER; x, y, w, h, xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdCanvasPutImageRectRGB"
         }"
      end

	int_canvas_c_double_put_image_rgb(wgt: POINTER; iw, ih: INTEGER; r, g, b: POINTER; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdfCanvasPutImageRectRGB"
         }"
      end

   int_wd_canvas_put_image_rgb(wgt: POINTER; iw, ih: INTEGER; r, g, b: POINTER; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "wdCanvasPutImageRectRGB"
         }"
      end

	int_canvas_put_image_rgba(wgt: POINTER; iw, ih: INTEGER; r, g, b, a: POINTER; x, y, w, h, xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdCanvasPutImageRectRGBA"
         }"
      end

	int_canvas_c_double_put_image_rgba(wgt: POINTER; iw, ih: INTEGER; r, g, b, a: POINTER; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdfCanvasPutImageRectRGBA"
         }"
      end

   int_wd_canvas_put_image_rgba(wgt: POINTER; iw, ih: INTEGER; r, g, b, a: POINTER; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "wdCanvasPutImageRectRGBA"
         }"
      end

	int_canvas_put_image_map(wgt: POINTER; iw, ih: INTEGER; i, c: POINTER; x, y, w, h, xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdCanvasPutImageRectMap"
         }"
      end

	int_canvas_c_double_put_image_map(wgt: POINTER; iw, ih: INTEGER; i, c: POINTER; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdfCanvasPutImageRectMap"
         }"
      end

   int_wd_canvas_put_image_map(wgt: POINTER; iw, ih: INTEGER; i, c: POINTER; x, y, w, h: REAL_64; xmin, xmax, ymin, ymax: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "wdCanvasPutImageRectMap"
         }"
      end

	int_canvas_get_im_image(wgt, img: POINTER; x, y: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdCanvasGetImImage"
         }"
      end

	int_wd_canvas_get_im_image(wgt, img: POINTER; x, y: REAL_64)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "wdCanvasGetImImage"
         }"
      end

	int_canvas_put_im_image(wgt, img: POINTER; x, y, w, h: INTEGER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdCanvasPutImImage"
         }"
      end

	int_canvas_c_double_put_im_image(wgt, img: POINTER; x, y, w, h: REAL_64)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdfCanvasPutImImage"
         }"
      end

	int_wd_canvas_put_im_image(wgt, img: POINTER; x, y, w, h: REAL_64)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "wdCanvasPutImImage"
         }"
      end

	int_rgb_to_map(iw, ih: INTEGER; r, g, b, i: POINTER; ps: INTEGER; cl: POINTER)
		external "plug_in"
      alias "{
		   location: "${sys}/plugins"
			module_name: "iup"
         feature_name: "cdRGB2Map"
         }"
      end

	int_get_native_handle_image(wgt: POINTER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupGetNativeHandleImage"
         }"
      end

	int_iup_image_from_im_image(wgt: POINTER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupImageFromImImage"
         }"
      end

	-- These features maybe should be removed in the future.
	
	int_im_image_create(w, h, cs, dt: INTEGER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "imImageCreate"
         }"
      end

	int_im_image_destroy(wgt: POINTER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "imImageDestroy"
         }"
      end
	
end

-- The MIT License (MIT)

-- Copyright (c) 2017 by German A. Arias

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in 
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
